package com.cancer.common.util;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;

import org.apache.http.NameValuePair;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.AbstractHttpEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

/**
 * http client工具。注意：该类不处理任何调用期间产生的异常
 * 
 * @company GeekPlus
 * @project beetle
 * @author 刘俊
 * @date 2016年11月18日
 * @since 1.0.0
 */
public final class HttpClientUtil {
	private static final Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);

	private static CloseableHttpClient client;
	static {
		PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
		// 连接池最大生成连接数
		cm.setMaxTotal(128);
		// 默认设置route最大连接数
		cm.setDefaultMaxPerRoute(32);
		// 创建httpClient
		client = HttpClients.custom().setConnectionManager(cm).build();
	}

	private HttpClientUtil() {
	}
	
	/**
	 * 发起GET请求
	 * 
	 * @author 刘俊 2016年11月22日
	 * @param url		请求地址
	 * @return
	 * @throws IOException IO异常
	 */
	public static CloseableHttpResponse sendGet(final String url) throws IOException {
		// 创建HttpPost
		HttpGet get = new HttpGet(url);
		
		// 发起请求
		return client.execute(get);
	}


	/**
	 * 发起get请求
	 * 
	 * @author 刘俊 2016年11月21日
	 * @param url		请求地址
	 * @param charset	编码，默认为UTF8
	 * @return
	 * @throws IllegalStateException 运行时异常，当http响应状态错误时
	 * @throws IOException IO异常
	 */
	public static String sendGet(final String url, final Charset charset) throws IOException {
		logger.info("sendPost - url:{}, charset:{}", url, charset);

		// 解析响应
		return HttpResponseHandler.handleHttpResponse(sendGet(url), charset);
	}
	
	/**
	 * 发起get请求
	 * 
	 * @author 刘俊 2016年11月21日
	 * @param url		请求地址
	 * @param charset	编码，默认为UTF8
	 * @return
	 * @throws IllegalStateException 运行时异常，当http响应状态错误时
	 * @throws IOException IO异常
	 */
	public static <T> T sendGet(final String url, final Charset charset, Class<T> type) throws IOException {
		logger.info("sendPost - url:{}, charset:{}", url, charset);
		
		// 解析响应
		return HttpResponseHandler.handleHttpResponse(sendGet(url), charset, type);
	}
	
	/**
	 * 发起get请求
	 * 
	 * @author 刘俊 2016年11月21日
	 * @param url		请求地址
	 * @param charset	编码，默认为UTF8
	 * @return
	 * @throws IllegalStateException 运行时异常，当http响应状态错误时
	 * @throws IOException IO异常
	 */
	public static <T> T sendGet(final String url, final Charset charset, TypeReference<T> type) throws IOException {
		logger.info("sendPost - url:{}, charset:{}", url, charset);
		
		// 解析响应
		return HttpResponseHandler.handleHttpResponse(sendGet(url), charset, type);
	}
	
	/**
	 * 发起POST请求，返回CloseableHttpResponse
	 * 
	 * @author 刘俊 2016年7月20日
	 * @param url		请求地址
	 * @param entity	参数
	 * @param charset	编码，默认为UTF8
	 * @return 
	 * @throws IllegalStateException 运行时异常，当http响应状态错误时
	 * @throws IOException IO异常
	 */
	public static CloseableHttpResponse sendPost(final String url, final AbstractHttpEntity entity) throws IOException {
		// 创建HttpPost
		HttpPost post = new HttpPost(url);
		if (entity != null) {
			post.setEntity(entity);
		}
		
		// 发起请求
		return client.execute(post);
	}

	/**
	 * 发起POST请求，返回String
	 * 
	 * @author 刘俊 2016年7月20日
	 * @param url		请求地址
	 * @param entity	参数
	 * @param charset	编码，默认为UTF8
	 * @return
	 * @throws IllegalStateException 运行时异常，当http响应状态错误时
	 * @throws IOException IO异常
	 */
	public static String sendPost(final String url, final AbstractHttpEntity entity, final Charset charset) throws IOException {
		logger.info("sendPost - url:{}, entity:{}, charset:{}", url, entity, charset);
		
		// 解析并返回
		return HttpResponseHandler.handleHttpResponse(sendPost(url, entity), charset);
	}
	
	/**
	 * 发起POST请求，返回指定类型
	 * 
	 * @author 刘俊 2016年7月20日
	 * @param url		请求地址
	 * @param entity	参数
	 * @param charset	编码，默认为UTF8
	 * @param type		类型
	 * @return
	 * @throws IllegalStateException 运行时异常，当http响应状态错误时
	 * @throws IOException IO异常
	 */
	public static <T> T sendPost(final String url, final AbstractHttpEntity entity, final Charset charset, Class<T> type) throws IOException {
		
		// 解析响应
		return HttpResponseHandler.handleHttpResponse(sendPost(url, entity), charset, type);
	}
	
	/**
	 * 发起POST请求，返回指定类型
	 * 
	 * @author 刘俊 2016年7月20日
	 * @param url		请求地址
	 * @param entity	参数
	 * @param charset	编码，默认为UTF8
	 * @param type		类型
	 * @return
	 * @throws IllegalStateException 运行时异常，当http响应状态错误时
	 * @throws IOException IO异常
	 */
	public static <T> T sendPost(final String url, final AbstractHttpEntity entity, final Charset charset, TypeReference<T> type) throws IOException {
		logger.info("sendPost - url:{}, entity:{}, charset:{}", url, entity, charset);
		
		// 解析响应
		return HttpResponseHandler.handleHttpResponse(sendPost(url, entity), charset, type);
	}
	
	/**
	 * http client 返回值处理
	 * 
	 * @company GeekPlus
	 * @project beetle
	 * @author 刘俊
	 * @date 2016年11月21日
	 * @since 1.0.0
	 */
	public static final class HttpResponseHandler {
		private static ObjectMapper mapper;
		static {
			mapper = new ObjectMapper();
			mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // 忽略json和bean的字段数量不匹配
			mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); //
//			mapper.configure(Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true); //解决json中存在转移字符Illegal unquoted character 
		}

		/**
		 * 解析http响应成字符串
		 * 
		 * @author 刘俊 2016年11月21日
		 * @param resp		响应
		 * @param charset	编码
		 * @return http响应信息
		 * @throws IllegalStateException 运行时异常，当http响应状态错误时
		 * @throws IOException IO异常
		 */
		public static String handleHttpResponse(CloseableHttpResponse resp, Charset charset) throws IOException {
			if (resp != null) {
				if (resp.getStatusLine().getStatusCode() == HttpStatus.OK.value()) {
					return EntityUtils.toString(resp.getEntity(), charset == null ? CharsetConst.UTF_8 : charset);
				} else {
					throw new IllegalStateException(String.format("%s", resp.getStatusLine()));
				}
			}
			return null;
		}

		/**
		 * 解析http响应成指定类型
		 * 
		 * @author 刘俊 2016年11月21日
		 * @param resp		响应
		 * @param charset	编码
		 * @return http响应信息
		 * @throws IllegalStateException 运行时异常，当http响应状态错误时
		 * @throws IOException IO异常
		 */
		public static <T> T handleHttpResponse(CloseableHttpResponse resp, Charset charset, Class<T> type) throws IOException {
			String content = handleHttpResponse(resp, charset);
			if (content != null) {
				return mapper.readValue(content, type);
			} else {
				return null;
			}
		}

		/**
		 * 解析http响应成指定类型
		 * 
		 * @author 刘俊 2016年11月21日
		 * @param resp		响应
		 * @param charset	编码
		 * @param type		类型
		 * @return
		 * @throws IOException
		 */
		public static <T> T handleHttpResponse(CloseableHttpResponse resp, Charset charset, TypeReference<T> type) throws IOException {
			String content = handleHttpResponse(resp, charset);
			if (content != null) {
				return mapper.readValue(content, type);
			} else {
				return null;
			}
		}
	}
	
	/**
	 * 键值对构造器
	 * 
	 * @company GeekPlus
	 * @project beetle
	 * @author 刘俊
	 * @date 2016年11月21日
	 * @since 1.0.0
	 */
	public static final class NameValuePairBuilder {
		private LinkedHashMap<String, String> queryParams = new LinkedHashMap<>();

		/** 构造器 **/
		public NameValuePairBuilder() {
		}

		/** 构造器 **/
		public NameValuePairBuilder(Map<?, ?> map) {
			for (Entry<?, ?> ent : map.entrySet()) {
				if (ent.getValue() == null) {
					continue;
				}
				this.put(ent.getKey().toString(), ent.getValue().toString());
			}
		}

		/** 构造器 **/
		public static NameValuePairBuilder create() {
			return new NameValuePairBuilder();
		}

		/** 构造器 **/
		public static NameValuePairBuilder create(Map<?, ?> map) {
			NameValuePairBuilder b = new NameValuePairBuilder();
			for (Entry<?, ?> ent : map.entrySet()) {
				if (ent.getValue() == null) {
					continue;
				}
				b.put(ent.getKey().toString(), ent.getValue().toString());
			}
			return b;
		}

		/** 存放键值对 **/
		public NameValuePairBuilder put(String key, String val) {
			queryParams.put(key, val);
			return this;
		}

		/** 根据键删除值 **/
		public NameValuePairBuilder remove(String key) {
			queryParams.remove(key);
			return this;
		}

		/** 获取到存储的map **/
		public Map<String, String> get() {

			return queryParams;
		}

		/** 获取NameValuePair **/
		public List<NameValuePair> build() {
			return queryParams.entrySet().stream()
					.map(e -> new BasicNameValuePair(e.getKey(), e.getValue()))
					.collect(Collectors.toList());
		}
	}
	
	/**
	 * URL构造器
	 * 
	 * @company GeekPlus
	 * @project beetle
	 * @author 刘俊
	 * @date 2016年11月21日
	 * @since 1.0.0
	 */
	public static final class URIBuilder {
		private String uri;
		private LinkedHashMap<String, Object> queryParams = new LinkedHashMap<>();

		/** 构造器 **/
		public URIBuilder() {
		}

		/** 构造器 **/
		public URIBuilder(String uri) {
			this.uri = uri;
		}

		public String getUri() {
			return uri;
		}

		public void setUri(String uri) {
			this.uri = uri;
		}

		/** 构造器 **/
		public static URIBuilder create() {
			return new URIBuilder();
		}

		/** 构造器 **/
		public static URIBuilder create(String uri) {
			URIBuilder b = new URIBuilder();
			b.setUri(uri);
			return b;
		}

		/** 存放键值对 **/
		public URIBuilder put(String key, Object val) {
			queryParams.put(key, val);
			return this;
		}

		/** 根据键删除值 **/
		public URIBuilder remove(String key) {
			queryParams.remove(key);
			return this;
		}

		/** 获取到存储的map **/
		public Map<String, Object> get() {

			return queryParams;
		}

		/** 获取URI **/
		public URI build() {
			StringBuilder params = new StringBuilder();
			if (uri != null) {
				params.append(uri);
				if (!uri.contains("?") && queryParams.size() > 0) {
					params.append('?');
					params.append(queryParams.entrySet().stream()
						.map(e -> e.getKey() + "=" + e.getValue())
						.collect(Collectors.joining("&")));
				}
			}
			
			try {
				return new URI(params.toString());
			} catch (URISyntaxException e) {
				throw new IllegalArgumentException(e);
			}
		}
		
		/** 获取URI **/
		public String buildUrl() {
			StringBuilder params = new StringBuilder();
			if (uri != null) {
				params.append(uri);
				if (!uri.contains("?") && queryParams.size() > 0) {
					params.append('?');
					params.append(queryParams.entrySet().stream()
							.map(e -> e.getKey() + "=" + e.getValue())
							.collect(Collectors.joining("&")));
				}
			}
			
			return params.toString();
		}
	}

}
